-
Notifications
You must be signed in to change notification settings - Fork 4.3k
feat(custom-resource): add External ID support for AwsCustomResource #35252
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
feat(custom-resource): add External ID support for AwsCustomResource #35252
Conversation
Adds external ID support to AwsCustomResource for enhanced security when assuming cross-account roles, helping prevent 'confused deputy' attacks. Changes: - Add optional property to AwsSdkCall interface - Modify Lambda handler to include ExternalId in STS AssumeRole calls - Update construct-types interface for Lambda handler compatibility - Add comprehensive unit tests covering all scenarios - Add integration tests for real-world usage - Update README with usage examples and security guidance Features: - External ID support for all lifecycle operations (onCreate, onUpdate, onDelete) - Works with region configuration and existing assumedRoleArn - Maintains full backward compatibility - Different external IDs for different operations - Proper security documentation and examples Security Enhancement: - Prevents confused deputy attacks in cross-account scenarios - Follows AWS security best practices for STS AssumeRole - Enables enterprise-grade multi-account infrastructure patterns Tests: - Unit tests: 10 comprehensive test cases - Integration tests: 4 real-world scenarios - Lambda handler tests: 7 utility function tests - Covers edge cases, security scenarios, and backward compatibility Fixes aws#34018
Simplify the integration test to follow existing patterns more closely and reduce complexity for snapshot generation.
…D support - Add comprehensive integration test snapshots for AwsCustomResource External ID feature - Include CloudFormation template demonstrating External ID usage in role assumption - Verify External ID prevents confused deputy attacks in cross-account scenarios - Satisfy PR linter requirements for integration test validation Addresses aws#34018
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(This review is outdated)
✅ Updated pull request passes all PRLinter validations. Dismissing previous PRLinter review.
.../@aws-cdk/custom-resource-handlers/lib/custom-resources/aws-custom-resource-handler/utils.ts
Outdated
Show resolved
Hide resolved
.../@aws-cdk/custom-resource-handlers/lib/custom-resources/aws-custom-resource-handler/utils.ts
Outdated
Show resolved
Hide resolved
packages/aws-cdk-lib/custom-resources/lib/aws-custom-resource/aws-custom-resource.ts
Show resolved
Hide resolved
…aws-custom-resource.ts Co-authored-by: aemada-aws <[email protected]>
Co-authored-by: aemada-aws <[email protected]>
Pull request has been modified.
Co-authored-by: aemada-aws <[email protected]>
- Remove unnecessary conditional logic for ExternalId - Improve type safety by removing 'any' type annotation - AWS SDK handles undefined ExternalId values gracefully
- Throw error if ExternalId is provided without assumedRoleArn - Prevents silent security misconfigurations - Addresses reviewer feedback for enhanced security validation
packages/aws-cdk-lib/custom-resources/lib/aws-custom-resource/aws-custom-resource.ts
Outdated
Show resolved
Hide resolved
- Add back the 'readonly externalId?: string;' property that was accidentally removed - Keep the updated JSDoc documentation - Addresses reviewer feedback about missing property
Add validation to throw an error when externalId is provided without assumedRoleArn in the getCredentials function.
Pull request has been modified.
- Remove trailing spaces on line 12 - Add missing trailing comma on line 47
|
Can you try running the build locally? |
Updates snapshots for all AwsCustomResource integration tests due to: - Lambda handler code changes for External ID support - Asset hash update from a73ebb32... to 89f8c437... - Dynamic AWS account reference in external-id test trust policy All custom-resources integration tests verified and passing.
@aemada-aws Test Results: Note: The integration test runner shows 17 tests in other packages (Elasticsearch, OpenSearch, Route53, etc.) detecting the same Lambda asset hash change. These are tests outside the custom-resources scope that happen to use AwsCustomResource internally. I haven't updated those snapshots to keep this PR focused on the External ID feature changes. Ready for your review! |
Update Lambda runtime from Fn::FindInMap to hardcoded nodejs22.x to match changes from main branch merge.
You need to also update those snapshots. The snapshots are there to help know what changed and if the change is unexpected. You just need to refresh the snapshots, no code change needed. |
I will update the snapshots and let you know when it ready for review. Thanks |
This comment was marked as outdated.
This comment was marked as outdated.
|
This PR cannot be merged because it has conflicts. Please resolve them. The PR will be considered stale and closed if it remains in an unmergeable state. |
|
@aemada-aws ready for review |



Issue # (if applicable)
Closes #34018.
Reason for this change
AWS customers deploying multi-account infrastructure often need to assume cross-account roles for AwsCustomResource operations. However, without proper security measures, this can lead to "confused deputy" attacks where a malicious actor tricks the service into performing actions on behalf of a different account.
External IDs are a security best practice recommended by AWS to prevent these attacks by requiring an additional secret value when assuming cross-account roles. Currently, AwsCustomResource supports assumedRoleArn but lacks the externalId parameter, forcing customers to choose between cross-account functionality and security best practices.
Description of changes
This change adds External ID support to AwsCustomResource to enable secure cross-account role assumptions:
Core Interface Changes:
Added optional externalId property to the AwsSdkCall interface in aws-custom-resource.ts
Enhanced the interface with comprehensive documentation explaining security benefits and usage patterns
Lambda Handler Implementation:
Modified utils.ts in custom-resource-handlers to pass External ID to STS AssumeRole calls
Updated construct-types.ts interface to maintain type safety between CDK construct and Lambda handler
Enhanced getCredentials function to include ExternalId parameter when provided
Security Features:
External ID support for all lifecycle operations (onCreate, onUpdate, onDelete)
Different external IDs can be specified for different operations
Maintains full backward compatibility - external ID is optional
Works seamlessly with existing assumedRoleArn and region configurations
Documentation and Examples:
Added comprehensive README section explaining External ID security benefits
Included practical examples for single and per-operation external ID usage
Added links to AWS IAM documentation for security best practices
Documented integration with cross-account scenarios
Design Decisions:
Made externalId an optional property to maintain backward compatibility
Follows existing pattern used by assumedRoleArn for consistency
External ID is only used when assumedRoleArn is specified
Enables different external IDs per operation for fine-grained security control
Alternatives Considered and Rejected:
Separate construct: Would fragment the API and require maintaining two similar constructs
Global external ID configuration: Less secure and flexible than per-operation configuration
Required external ID: Would break backward compatibility for existing users
Describe any new or updated permissions being added
No new IAM permissions are required for this feature. The External ID is a security parameter used during the existing STS AssumeRole operation and does not require additional permissions.
The feature works within the existing permission model:
The Lambda function still uses its existing IAM role
The assumedRoleArn role requires the same permissions as before
External ID is validated by STS as part of the standard AssumeRole process
No additional AWS service calls or permissions are needed
Description of how you validated changes
Unit Testing (10 comprehensive test cases):
External ID parameter passing through to CloudFormation template
Different external IDs for different lifecycle operations
Backward compatibility when external ID is not specified
Integration with existing assumedRoleArn functionality
CloudFormation template generation with correct parameters
Edge cases and error handling scenarios
Integration Tests (4 real-world scenarios):
Cross-account role assumption with external ID
STS GetCallerIdentity operation with external ID validation
Integration test with proper CDK snapshot validation
End-to-end workflow demonstrating security enhancement
Lambda Handler Tests (7 utility function tests):
getCredentials function correctly passes External ID to STS
AssumeRole call includes ExternalId parameter when provided
Backward compatibility when external ID is not specified
Type safety between construct interface and Lambda implementation
Manual Testing:
Deployed test stack with cross-account External ID configuration
Verified STS AssumeRole calls include External ID parameter
Confirmed prevention of confused deputy attack scenarios
Validated integration with existing AWS CDK patterns
Security Validation:
Reviewed AWS security documentation alignment
Tested confused deputy attack prevention
Validated with enterprise multi-account use cases
Checklist
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license